GCC (GNU Compiler Collection) è una suite di compilatori sviluppata dal progetto GNU e distribuita con licenza GPL. Originariamente creato come compilatore C (GNU C Compiler), GCC si è evoluto diventando una collezione di compilatori per diversi linguaggi di programmazione.
GCC supporta nativamente i seguenti linguaggi:
GCC opera attraverso diverse fasi:
Prima di installare, verificare se GCC è già presente nel sistema:
gcc --version
Se non installato, si procede con l’installazione.
Su distribuzioni Debian/Ubuntu utilizzare il package manager apt:
# Aggiornare i repository
sudo apt update
# Installare GCC
sudo apt install gcc
# Per installare anche g++ (compilatore C++)
sudo apt install g++
# Per installare l'intera suite build-essential (consigliato)
sudo apt install build-essential
Il pacchetto build-essential include:
# Per Fedora
sudo dnf install gcc gcc-c++
# Per CentOS/RHEL
sudo yum install gcc gcc-c++
# Per installare l'intero Development Tools group
sudo yum groupinstall "Development Tools"
sudo pacman -S gcc
Per installare versioni multiple di GCC:
# Installare GCC versione specifica (esempio: GCC 11)
sudo apt install gcc-11 g++-11
# Configurare alternative per gestire versioni multiple
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 110
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-11 110
# Selezionare la versione da utilizzare
sudo update-alternatives --config gcc
sudo update-alternatives --config g++
Questo metodo installa Clang (compilatore compatibile con GCC) che viene invocato attraverso il comando gcc:
# Installare Command Line Tools
xcode-select --install
Apparirà una finestra di dialogo per confermare l’installazione. Questo metodo è il più semplice e fornisce un ambiente di sviluppo completo.
Nota importante: Su macOS, il comando gcc è tipicamente un alias per clang, non il vero GCC GNU. Clang è compatibile al 99% con GCC per la maggior parte degli usi.
Per installare il vero GCC GNU:
# Installare Homebrew se non presente
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Installare GCC
brew install gcc
# Verificare l'installazione
gcc-13 --version # Il numero può variare in base alla versione installata
Con Homebrew, il vero GCC sarà disponibile come gcc-13, gcc-12, etc. per evitare conflitti con Clang.
Se necessario, aggiungere GCC al PATH modificando ~/.zshrc (o ~/.bash_profile per bash):
# Aggiungere al file di configurazione shell
export PATH="/usr/local/bin:$PATH"
# Ricaricare la configurazione
source ~/.zshrc
MinGW-w64 (Minimalist GNU for Windows) è una porta di GCC per Windows.
Passo 1: Scaricare MinGW-w64
Visitare il sito ufficiale: https://www.mingw-w64.org/downloads/
Opzioni principali:
Passo 2: Installazione con MSYS2 (metodo consigliato)
Scaricare MSYS2 da: https://www.msys2.org/
Eseguire l’installer msys2-x86_64-[data].exe
Seguire le istruzioni mantenendo il percorso predefinito: C:\msys64
Completare l’installazione e aprire il terminale MSYS2
Aggiornare i pacchetti:
pacman -Syu
pacman -Su
# Per architettura 64-bit
pacman -S mingw-w64-x86_64-gcc
# Per architettura 32-bit
pacman -S mingw-w64-i686-gcc
# Per installare l'intera suite di sviluppo
pacman -S --needed base-devel mingw-w64-x86_64-toolchain
Passo 3: Configurare le Variabili d’Ambiente
C:\msys64\mingw64\bin (per 64-bit)Passo 4: Verificare l’installazione
Aprire un nuovo Prompt dei comandi (cmd) o PowerShell:
gcc --version
g++ --version
C:\mingw64)C:\mingw64\bin alle variabili d’ambiente PATHgcc --versionUn’alternativa moderna è utilizzare WSL per avere un ambiente Linux completo su Windows:
# In PowerShell con privilegi amministrativi
wsl --install -d Ubuntu
# Dopo il riavvio e la configurazione di Ubuntu
sudo apt update
sudo apt install build-essential
setup-x86_64.exeDopo l’installazione, verificare che GCC funzioni correttamente.
# Verificare versione GCC
gcc --version
# Verificare versione G++
g++ --version
# Verificare altri compilatori se installati
gfortran --version
Creare un file di test hello.c:
#include <stdio.h>
int main() {
printf("GCC funziona correttamente!\n");
return 0;
}
Compilare ed eseguire:
# Compilazione
gcc hello.c -o hello
# Esecuzione (Linux/macOS)
./hello
# Esecuzione (Windows)
hello.exe
Se l’output è “GCC funziona correttamente!”, l’installazione è riuscita.
Per visualizzare informazioni dettagliate sulla configurazione di GCC:
# Visualizzare la configurazione di compilazione di GCC
gcc -v
# Mostrare le directory di ricerca degli include
gcc -E -x c - -v < /dev/null # Linux/macOS
echo | gcc -E -x c - -v # Windows
# Mostrare le directory di ricerca delle librerie
gcc -print-search-dirs
# Visualizzare i target supportati
gcc -dumpmachine
La sintassi base di GCC è:
gcc [opzioni] file_sorgente -o file_output
// programma.c
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
}
Compilazione:
# Compilazione base
gcc programma.c
# Questo crea un eseguibile chiamato 'a.out' su Linux/macOS
# o 'a.exe' su Windows
# Usare l'opzione -o per specificare il nome
gcc programma.c -o programma
# Eseguire
./programma # Linux/macOS
programma.exe # Windows
In un singolo comando, GCC esegue:
Per file C++ usare g++:
// programma.cpp
#include <iostream>
int main() {
std::cout << "Hello, C++!" << std::endl;
return 0;
}
Compilazione:
g++ programma.cpp -o programma
Nota: Anche se tecnicamente si può usare gcc per compilare C++, g++ è preferibile perché linka automaticamente le librerie standard C++.
# File singolo
gcc programma.c -o programma
# Con standard specifico
gcc -std=c99 programma.c -o programma # Standard C99
gcc -std=c11 programma.c -o programma # Standard C11
gcc -std=c17 programma.c -o programma # Standard C17 (più recente)
Standard C supportati:
c89, c90: ANSI C / ISO C90c99: ISO C99c11: ISO C11c17, c18: ISO C17gnu89, gnu90, gnu99, gnu11, gnu17: varianti GNU con estensioni# File singolo C++
g++ programma.cpp -o programma
# Con standard specifico
g++ -std=c++98 programma.cpp -o programma # C++98
g++ -std=c++11 programma.cpp -o programma # C++11
g++ -std=c++14 programma.cpp -o programma # C++14
g++ -std=c++17 programma.cpp -o programma # C++17
g++ -std=c++20 programma.cpp -o programma # C++20
g++ -std=c++23 programma.cpp -o programma # C++23 (sperimentale)
Standard C++ supportati:
c++98, c++03: Standard inizialic++11: Introduce auto, lambda, threadingc++14: Miglioramenti a C++11c++17: Filesystem, optional, variantc++20: Concepts, coroutine, rangesc++23: In sviluppognu++98, gnu++11, etc.: varianti GNU# Installare gfortran se non presente
sudo apt install gfortran # Ubuntu/Debian
# Compilazione
gfortran programma.f90 -o programma
gfortran programma.f -o programma # Fortran 77 (fixed format)
gfortran programma.f90 -o programma # Fortran 90+ (free format)
# Objective-C (principalmente su macOS)
gcc -lobjc programma.m -o programma
# Objective-C++
g++ -lobjc programma.mm -o programma
# Compilare con GNAT
gnatmake programma.adb
# Oppure
gcc -c programma.adb
gnatbind programma
gnatlink programma
# Con gccgo
gccgo programma.go -o programma
# Nota: è più comune usare il compilatore Go standard (gc)
# Con gdc
gdc programma.d -o programma
GCC permette di controllare individualmente le quattro fasi della compilazione.
Il preprocessing espande le macro e include i file header.
# Solo preprocessing, output su stdout
gcc -E programma.c
# Salvare il risultato in un file
gcc -E programma.c -o programma.i
Cosa fa il preprocessor:
#include direttive#define#ifdef, #ifndef, #ifEsempio:
// test.c
#define PI 3.14159
#include <stdio.h>
int main() {
double r = PI * 2.0;
return 0;
}
gcc -E test.c -o test.i
# Il file test.i conterrà tutto stdio.h espanso e PI sostituito con 3.14159
Genera codice assembly dal codice sorgente preprocessato.
# Genera file assembly (.s)
gcc -S programma.c
# Output sarà programma.s
Il file .s contiene istruzioni assembly specifiche per l’architettura target.
Esempio di output assembly (estratto):
main:
pushq %rbp
movq %rsp, %rbp
movl $0, %eax
popq %rbp
ret
Converte il codice assembly in codice oggetto (binario non linkato).
# Genera file oggetto (.o)
gcc -c programma.c
# Output sarà programma.o
Il file .o (.obj su Windows) contiene codice macchina ma non è ancora eseguibile perché mancano i collegamenti alle librerie.
Collega i file oggetto con le librerie necessarie per creare l’eseguibile finale.
# Linking di file oggetto
gcc programma.o -o programma
# Linking di più file oggetto
gcc file1.o file2.o file3.o -o programma
Eseguire tutte le fasi manualmente:
# 1. Preprocessing
gcc -E programma.c -o programma.i
# 2. Compilation
gcc -S programma.i -o programma.s
# 3. Assembly
gcc -c programma.s -o programma.o
# 4. Linking
gcc programma.o -o programma
# Eseguire
./programma
Questa separazione è utile per:
Specifica il nome del file di output.
gcc programma.c -o mio_programma
Compila o assembla il codice sorgente ma non linka. Produce file oggetto .o.
gcc -c file1.c # Produce file1.o
gcc -c file2.c # Produce file2.o
gcc file1.o file2.o -o programma # Link finale
Compila ma non assembla. Produce file assembly .s.
gcc -S programma.c # Produce programma.s
Solo preprocessing. Output su stdout o file specificato.
gcc -E programma.c -o programma.i
Specifica lo standard del linguaggio da utilizzare.
# C
gcc -std=c99 programma.c -o programma
gcc -std=c11 programma.c -o programma
# C++
g++ -std=c++11 programma.cpp -o programma
g++ -std=c++17 programma.cpp -o programma
Equivale a -std=c90 per C o -std=c++98 per C++.
gcc -ansi programma.c -o programma
Emette tutti i warning richiesti dallo standard ISO.
gcc -std=c11 -pedantic programma.c -o programma
Come -pedantic, ma emette errori invece di warning.
gcc -std=c11 -pedantic-errors programma.c -o programma
Aggiunge una directory al percorso di ricerca degli header file.
gcc -I/path/to/headers programma.c -o programma
gcc -I./include -I./lib/include programma.c -o programma
Esempio struttura progetto:
progetto/
├── src/
│ └── main.c
├── include/
│ └── mylib.h
└── lib/
└── mylib.c
Compilazione:
gcc -I./include src/main.c lib/mylib.c -o programma
Definisce una macro come se fosse scritta con #define.
# Definire macro booleana
gcc -DDEBUG programma.c -o programma
# Definire macro con valore
gcc -DVERSION=2 -DMAX_SIZE=1024 programma.c -o programma
Esempio di utilizzo:
#include <stdio.h>
int main() {
#ifdef DEBUG
printf("Modalità debug attiva\n");
#endif
#ifdef VERSION
printf("Versione: %d\n", VERSION);
#endif
return 0;
}
Compilazione:
gcc -DDEBUG -DVERSION=2 programma.c -o programma
Rimuove una definizione di macro predefinita.
gcc -U__STRICT_ANSI__ programma.c -o programma
Specifica una libreria da linkare. GCC cerca lib<libreria>.a o lib<libreria>.so.
# Linkare libreria matematica (libm)
gcc programma.c -o programma -lm
# Linkare più librerie
gcc programma.c -o programma -lm -lpthread -lssl -lcrypto
Esempio con matematica:
#include <stdio.h>
#include <math.h>
int main() {
double result = sqrt(16.0);
printf("Radice di 16 = %.2f\n", result);
return 0;
}
Compilazione:
gcc -o programma programma.c -lm
Nota: L’ordine è importante! Le librerie dipendenti devono essere specificate dopo quelle che le usano.
Aggiunge una directory al percorso di ricerca delle librerie.
gcc -L/usr/local/lib -L./lib programma.c -o programma -lmylib
Linka le librerie staticamente invece che dinamicamente.
gcc -static programma.c -o programma
Crea una libreria condivisa (shared library).
gcc -shared -fPIC mylib.c -o libmylib.so
Genera codice per architettura a 32 o 64 bit.
# 32-bit
gcc -m32 programma.c -o programma32
# 64-bit
gcc -m64 programma.c -o programma64
Ottimizza per un’architettura CPU specifica.
# Architettura nativa (quella del sistema corrente)
gcc -march=native programma.c -o programma
# Specifiche architetture
gcc -march=x86-64 programma.c -o programma
gcc -march=armv7-a programma.c -o programma
Ottimizza per una specifica CPU mantenendo compatibilità.
gcc -mtune=intel programma.c -o programma
gcc -mtune=generic programma.c -o programma
Modalità verbose: mostra tutti i comandi eseguiti da GCC.
gcc -v programma.c -o programma
Usa pipe invece di file temporanei durante la compilazione (più veloce).
gcc -pipe programma.c -o programma
Conserva i file intermedi (.i, .s, .o).
gcc -save-temps programma.c -o programma
# Crea: programma.i, programma.s, programma.o, programma
Mostra le opzioni disponibili.
gcc --help
gcc --help=optimizers # Help sulle opzioni di ottimizzazione
gcc --help=warnings # Help sui warning
Le ottimizzazioni migliorano le prestazioni del codice generato a scapito di tempi di compilazione più lunghi.
Nessuna ottimizzazione. Compilazione più rapida, debug più facile.
gcc -O0 programma.c -o programma
# Equivalente a non specificare alcuna opzione -O
Ottimizzazioni base. Riduce dimensione e tempo di esecuzione senza aumentare molto il tempo di compilazione.
gcc -O1 programma.c -o programma
# oppure
gcc -O programma.c -o programma
Attiva ottimizzazioni come:
Ottimizzazioni più aggressive (raccomandato per produzione).
gcc -O2 programma.c -o programma
Include tutte le ottimizzazioni di -O1 più:
È il livello raccomandato per la maggior parte delle applicazioni in produzione.
Massime ottimizzazioni. Può aumentare significativamente la dimensione del codice.
gcc -O3 programma.c -o programma
Include tutte le ottimizzazioni di -O2 più:
Attenzione: può occasionalmente introdurre bug in codice mal scritto.
Ottimizza per dimensione del codice.
gcc -Os programma.c -o programma
Utile per sistemi embedded o quando la dimensione del codice è critica.
Massime prestazioni ignorando la conformità stretta agli standard.
gcc -Ofast programma.c -o programma
Include -O3 più ottimizzazioni che possono violare la conformità IEEE/ISO:
-ffast-math: ottimizzazioni matematiche aggressiveOttimizzazioni compatibili con il debugging.
gcc -Og -g programma.c -o programma
Mantiene buone prestazioni senza compromettere l’esperienza di debug.
Inlining aggressivo delle funzioni.
gcc -O2 -finline-functions programma.c -o programma
Srotola i loop per ridurre overhead delle iterazioni.
gcc -O2 -funroll-loops programma.c -o programma
Omette il frame pointer quando non necessario, liberando un registro.
gcc -O2 -fomit-frame-pointer programma.c -o programma
Abilita la vectorizzazione automatica (inclusa in -O3).
gcc -O2 -ftree-vectorize programma.c -o programma
Ottimizzazioni matematiche aggressive che possono violare IEEE 754.
gcc -O2 -ffast-math programma.c -o programma
Attenzione: può causare comportamenti numerici imprevisti.
Ottimizzazione durante il linking attraverso tutto il programma.
# Compilazione con LTO
gcc -flto -O2 file1.c file2.c -o programma
# Per progetti grandi, compilare separatamente
gcc -flto -O2 -c file1.c
gcc -flto -O2 -c file2.c
gcc -flto -O2 file1.o file2.o -o programma
LTO permette ottimizzazioni cross-file che non sarebbero possibili altrimenti.
Ottimizzazione basata su dati di profilazione reali.
# Passo 1: Compilare con instrumentazione
gcc -O2 -fprofile-generate programma.c -o programma
# Passo 2: Eseguire il programma con dati rappresentativi
./programma < dati_test.txt
# Passo 3: Ricompilare usando i dati raccolti
gcc -O2 -fprofile-use programma.c -o programma_ottimizzato
# Cleanup
rm -f *.gcda
Esempio di benchmark:
// test_performance.c
#include <stdio.h>
#include <time.h>
#define SIZE 10000000
int main() {
clock_t start = clock();
long long sum = 0;
for (int i = 0; i < SIZE; i++) {
sum += i * i;
}
clock_t end = clock();
double time_spent = (double)(end - start) / CLOCKS_PER_SEC;
printf("Somma: %lld\n", sum);
printf("Tempo: %.6f secondi\n", time_spent);
return 0;
}
Compilare con diversi livelli:
gcc -O0 test_performance.c -o test_O0
gcc -O1 test_performance.c -o test_O1
gcc -O2 test_performance.c -o test_O2
gcc -O3 test_performance.c -o test_O3
# Testare
time ./test_O0
time ./test_O1
time ./test_O2
time ./test_O3
Genera informazioni di debug per il debugger (gdb).
gcc -g programma.c -o programma
Livelli di debug:
-g0: Nessuna informazione di debug-g1: Informazioni minime-g2: Informazioni di debug standard (default per -g)-g3: Informazioni extra (macro definitions)gcc -g3 programma.c -o programma
Ottimizza le informazioni di debug per GDB.
gcc -ggdb3 programma.c -o programma
Specifica la versione del formato DWARF per il debug.
gcc -g -gdwarf-4 programma.c -o programma
Combinare debug e ottimizzazione:
# Debug-friendly optimization
gcc -Og -g programma.c -o programma
# O anche O2 con debug (esperienza di debug degradata)
gcc -O2 -g programma.c -o programma
I warning aiutano a identificare potenziali problemi nel codice.
Abilita un set completo di warning comuni.
gcc -Wall programma.c -o programma
Include warning per:
Warning addizionali non inclusi in -Wall.
gcc -Wall -Wextra programma.c -o programma
Include:
Tratta tutti i warning come errori (ferma la compilazione).
gcc -Wall -Wextra -Werror programma.c -o programma
Utile per garantire codice senza warning.
Tratta uno specifico warning come errore.
gcc -Wall -Werror=return-type programma.c -o programma
Warning per codice non conforme allo standard.
gcc -std=c11 -Wpedantic programma.c -o programma
# Warning su variabili non inizializzate
gcc -Wuninitialized programma.c -o programma
# Warning su conversioni implicite
gcc -Wconversion programma.c -o programma
# Warning su shadowing di variabili
gcc -Wshadow programma.c -o programma
# Warning su funzioni deprecate
gcc -Wdeprecated-declarations programma.c -o programma
# Warning su formato printf/scanf errato
gcc -Wformat programma.c -o programma
# Warning combinati (configurazione rigorosa)
gcc -Wall -Wextra -Wpedantic -Wshadow -Wconversion \
-Wuninitialized programma.c -o programma
Disabilita uno specifico warning.
# Disabilitare warning su variabili inutilizzate
gcc -Wall -Wno-unused-variable programma.c -o programma
Disabilita tutti i warning (sconsigliato).
gcc -w programma.c -o programma
I sanitizers rilevano errori a runtime.
Rileva errori di memoria: buffer overflow, use-after-free, memory leaks.
gcc -fsanitize=address -g programma.c -o programma
Esempio:
// bug.c
#include <stdlib.h>
int main() {
int *array = malloc(10 * sizeof(int));
array[10] = 42; // Buffer overflow!
free(array);
return 0;
}
gcc -fsanitize=address -g bug.c -o bug
./bug
# Output mostra il buffer overflow con stack trace
Rileva undefined behavior: overflow aritmetico, null pointer dereference, ecc.
gcc -fsanitize=undefined -g programma.c -o programma
Rileva data race in programmi multi-thread.
gcc -fsanitize=thread -g programma.c -o programma -lpthread
Rileva memory leak.
gcc -fsanitize=leak -g programma.c -o programma
# Address + Undefined Behavior
gcc -fsanitize=address,undefined -g programma.c -o programma
Nota: ThreadSanitizer non può essere combinato con AddressSanitizer.
Analisi statica del codice (GCC 10+).
gcc -fanalyzer programma.c -o programma
Rileva:
Genera regole di dipendenza per Make.
# Include dipendenze di sistema
gcc -M programma.c
# Esclude dipendenze di sistema
gcc -MM programma.c
Output:
programma.o: programma.c myheader.h
Come -M e -MM ma compila anche il file.
gcc -MMD -c programma.c
# Crea programma.o e programma.d (file dipendenze)
Specifica il file di output per le dipendenze.
gcc -MMD -MF programma.dep -c programma.c
Include automaticamente un file header prima della compilazione.
gcc -include config.h programma.c -o programma
Include le macro da un file senza includerne il contenuto.
gcc -imacros macros.h programma.c -o programma
Genera Position Independent Code per librerie condivise.
gcc -fPIC -c mylib.c -o mylib.o
gcc -shared mylib.o -o libmylib.so
-fPIC è preferito per compatibilità, -fpic può essere più efficiente ma ha limitazioni.
Ogni variabile globale non inizializzata viene allocata nel segmento BSS.
gcc -fno-common programma.c -o programma
Utile per rilevare multiple definizioni.
Protezione contro buffer overflow dello stack.
# Protezione base
gcc -fstack-protector programma.c -o programma
# Protezione per tutte le funzioni
gcc -fstack-protector-all programma.c -o programma
# Protezione forte (bilanciamento tra -all e base)
gcc -fstack-protector-strong programma.c -o programma
Ulteriore protezione contro buffer overflow (richiede -O1 o superiore).
gcc -O2 -D_FORTIFY_SOURCE=2 programma.c -o programma
Passa opzioni direttamente al linker.
# Specificare rpath
gcc programma.c -o programma -Wl,-rpath,/usr/local/lib
# Settare soname per libreria condivisa
gcc -shared -Wl,-soname,libmylib.so.1 mylib.o -o libmylib.so.1.0
# Strippare simboli di debug al linking
gcc programma.c -o programma -Wl,--strip-all
Non linka la libreria standard C.
gcc -nostdlib -nostartfiles programma.c -o programma
Usato per kernel, bootloader, o embedded systems.
Linka staticamente le librerie GCC/G++.
gcc -static-libgcc programma.c -o programma
g++ -static-libstdc++ programma.cpp -o programma
Aggiunge tutti i simboli alla tabella dei simboli dinamici.
gcc -rdynamic programma.c -o programma -ldl
Utile per backtrace e plugin dinamici.
Abilita istruzioni SSE specifiche.
gcc -msse4.2 programma.c -o programma
Abilita istruzioni AVX.
gcc -mavx2 programma.c -o programma
Specifica l’unità floating-point (ARM).
gcc -mfpu=neon programma.c -o programma
GCC definisce automaticamente diverse macro:
#include <stdio.h>
int main() {
printf("GCC Version: %d.%d.%d\n",
__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
printf("Standard C: %ld\n", __STDC_VERSION__);
#ifdef __linux__
printf("Sistema: Linux\n");
#elif defined(__APPLE__)
printf("Sistema: macOS\n");
#elif defined(_WIN32)
printf("Sistema: Windows\n");
#endif
printf("File: %s\n", __FILE__);
printf("Linea: %d\n", __LINE__);
printf("Data compilazione: %s\n", __DATE__);
printf("Ora compilazione: %s\n", __TIME__);
return 0;
}
Compilare:
gcc info.c -o info
./info
Funzioni eseguite prima/dopo main.
#include <stdio.h>
__attribute__((constructor))
void before_main() {
printf("Eseguito prima di main()\n");
}
__attribute__((destructor))
void after_main() {
printf("Eseguito dopo main()\n");
}
int main() {
printf("Dentro main()\n");
return 0;
}
Strutture senza padding.
struct __attribute__((packed)) PackedStruct {
char a;
int b;
char c;
};
Allineamento specifico.
int var __attribute__((aligned(16)));
Sopprime warning per variabili/parametri non usati.
void func(int x __attribute__((unused))) {
// x non usato, ma nessun warning
}
# File sorgenti
# mymath.c
// mymath.c
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
// mymath.h
#ifndef MYMATH_H
#define MYMATH_H
int add(int a, int b);
int subtract(int a, int b);
#endif
Creazione della libreria:
# Compilare in file oggetto
gcc -c mymath.c -o mymath.o
# Creare archivio statico
ar rcs libmymath.a mymath.o
# Verificare contenuto
ar -t libmymath.a
// main.c
#include <stdio.h>
#include "mymath.h"
int main() {
printf("5 + 3 = %d\n", add(5, 3));
printf("5 - 3 = %d\n", subtract(5, 3));
return 0;
}
Compilazione:
# Metodo 1: Specificare la libreria
gcc main.c -L. -lmymath -o programma
# Metodo 2: Includere direttamente
gcc main.c libmymath.a -o programma
# Eseguire
./programma
# Compilare con Position Independent Code
gcc -fPIC -c mymath.c -o mymath.o
# Creare libreria condivisa
gcc -shared mymath.o -o libmymath.so
# Con soname (versioning)
gcc -shared -Wl,-soname,libmymath.so.1 mymath.o -o libmymath.so.1.0.0
# Creare symlink
ln -s libmymath.so.1.0.0 libmymath.so.1
ln -s libmymath.so.1 libmymath.so
gcc -fPIC -c mymath.c -o mymath.o
gcc -dynamiclib mymath.o -o libmymath.dylib
gcc -shared mymath.c -o mymath.dll
# Compilare
gcc main.c -L. -lmymath -o programma
# Eseguire (Linux) - se non trova la libreria
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
./programma
# O installare in directory sistema
sudo cp libmymath.so /usr/local/lib/
sudo ldconfig
Su macOS:
export DYLD_LIBRARY_PATH=.:$DYLD_LIBRARY_PATH
./programma
Su Windows: assicurarsi che mymath.dll sia nella stessa directory dell’eseguibile o in una directory nel PATH.
# Compilare con rpath embedded
gcc main.c -L. -lmymath -Wl,-rpath,'$ORIGIN' -o programma
# O specificare percorso assoluto
gcc main.c -L. -lmymath -Wl,-rpath,/usr/local/lib -o programma
$ORIGIN significa “directory dell’eseguibile”.
#include <stdio.h>
#include <math.h>
int main() {
printf("sqrt(16) = %.2f\n", sqrt(16.0));
printf("sin(0) = %.2f\n", sin(0.0));
printf("pow(2, 8) = %.0f\n", pow(2.0, 8.0));
return 0;
}
Compilazione:
gcc programma.c -o programma -lm
#include <stdio.h>
#include <pthread.h>
void* thread_function(void* arg) {
printf("Thread in esecuzione\n");
return NULL;
}
int main() {
pthread_t thread;
pthread_create(&thread, NULL, thread_function, NULL);
pthread_join(thread, NULL);
return 0;
}
Compilazione:
gcc programma.c -o programma -lpthread
#include <openssl/md5.h>
#include <stdio.h>
#include <string.h>
int main() {
unsigned char digest[MD5_DIGEST_LENGTH];
char string[] = "hello";
MD5((unsigned char*)string, strlen(string), digest);
printf("MD5 hash: ");
for(int i = 0; i < MD5_DIGEST_LENGTH; i++)
printf("%02x", digest[i]);
printf("\n");
return 0;
}
Compilazione:
gcc programma.c -o programma -lssl -lcrypto
progetto/
├── include/
│ ├── util.h
│ └── calc.h
├── src/
│ ├── main.c
│ ├── util.c
│ └── calc.c
├── lib/
│ └── (librerie esterne)
├── obj/
│ └── (file oggetto temporanei)
└── Makefile
include/calc.h
#ifndef CALC_H
#define CALC_H
int add(int a, int b);
int multiply(int a, int b);
#endif
include/util.h
#ifndef UTIL_H
#define UTIL_H
void print_result(const char* operation, int result);
#endif
src/calc.c
#include "calc.h"
int add(int a, int b) {
return a + b;
}
int multiply(int a, int b) {
return a * b;
}
src/util.c
#include <stdio.h>
#include "util.h"
void print_result(const char* operation, int result) {
printf("%s = %d\n", operation, result);
}
src/main.c
#include <stdio.h>
#include "calc.h"
#include "util.h"
int main() {
int a = 10, b = 5;
int sum = add(a, b);
print_result("10 + 5", sum);
int product = multiply(a, b);
print_result("10 * 5", product);
return 0;
}
gcc -I./include src/main.c src/calc.c src/util.c -o programma
# Creare directory per oggetti
mkdir -p obj
# Compilare ogni file in oggetto
gcc -I./include -c src/main.c -o obj/main.o
gcc -I./include -c src/calc.c -o obj/calc.o
gcc -I./include -c src/util.c -o obj/util.o
# Linkare tutti gli oggetti
gcc obj/main.o obj/calc.o obj/util.o -o programma
# Eseguire
./programma
Vantaggi:
Makefile
# Compilatore e flag
CC = gcc
CFLAGS = -Wall -Wextra -I./include -O2 -g
LDFLAGS =
# Directory
SRC_DIR = src
OBJ_DIR = obj
INC_DIR = include
# File
SOURCES = $(wildcard $(SRC_DIR)/*.c)
OBJECTS = $(SOURCES:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)
TARGET = programma
# Regola principale
all: $(TARGET)
# Linking
$(TARGET): $(OBJECTS)
$(CC) $(OBJECTS) -o $@ $(LDFLAGS)
# Compilazione
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
$(CC) $(CFLAGS) -c $< -o $@
# Creare directory oggetti
$(OBJ_DIR):
mkdir -p $(OBJ_DIR)
# Pulizia
clean:
rm -rf $(OBJ_DIR) $(TARGET)
# Rebuild completo
rebuild: clean all
# Regole di debug
debug: CFLAGS += -DDEBUG -g3
debug: all
# Regole di release
release: CFLAGS += -O3 -DNDEBUG
release: all
.PHONY: all clean rebuild debug release
Uso:
# Compilare
make
# Compilare versione debug
make debug
# Compilare versione release ottimizzata
make release
# Pulire
make clean
# Rebuild completo
make rebuild
CC = gcc
CFLAGS = -Wall -Wextra -I./include -MMD -MP
LDFLAGS = -lm
SRC_DIR = src
OBJ_DIR = obj
INC_DIR = include
SOURCES = $(wildcard $(SRC_DIR)/*.c)
OBJECTS = $(SOURCES:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)
DEPENDS = $(OBJECTS:.o=.d)
TARGET = programma
all: $(TARGET)
$(TARGET): $(OBJECTS)
$(CC) $(OBJECTS) -o $@ $(LDFLAGS)
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
$(CC) $(CFLAGS) -c $< -o $@
$(OBJ_DIR):
mkdir -p $(OBJ_DIR)
clean:
rm -rf $(OBJ_DIR) $(TARGET)
-include $(DEPENDS)
.PHONY: all clean
L’opzione -MMD -MP genera automaticamente le dipendenze dagli header file.
Compilare per una piattaforma diversa da quella di sviluppo.
# Per ARM su Ubuntu
sudo apt install gcc-arm-linux-gnueabihf
# Per Windows su Linux (MinGW)
sudo apt install mingw-w64
# Per architetture multiple
sudo apt install gcc-multilib g++-multilib
# Compilare per ARM
arm-linux-gnueabihf-gcc programma.c -o programma_arm
# Compilare per Windows da Linux
x86_64-w64-mingw32-gcc programma.c -o programma.exe
# Compilare per 32-bit su sistema 64-bit
gcc -m32 programma.c -o programma32
# Impostare compilatore e flag
export CC=arm-linux-gnueabihf-gcc
export CXX=arm-linux-gnueabihf-g++
export CFLAGS="-march=armv7-a"
# Poi usare normalmente
$CC programma.c -o programma
// myheader.h
#ifndef MYHEADER_H
#define MYHEADER_H
// Contenuto header
#endif /* MYHEADER_H */
Oppure usare #pragma once (non standard ma ampiamente supportato):
// myheader.h
#pragma once
// Contenuto header
#include <stdio.h>
int main() {
#ifdef _WIN32
printf("Windows\n");
#elif defined(__linux__)
printf("Linux\n");
#elif defined(__APPLE__) && defined(__MACH__)
printf("macOS\n");
#else
printf("Sistema sconosciuto\n");
#endif
return 0;
}
#include <stdio.h>
#ifdef DEBUG
#define LOG(msg) printf("[DEBUG] %s:%d - %s\n", __FILE__, __LINE__, msg)
#else
#define LOG(msg)
#endif
int main() {
LOG("Inizio programma");
printf("Hello, World!\n");
LOG("Fine programma");
return 0;
}
Compilazione:
# Versione debug
gcc -DDEBUG programma.c -o programma_debug
# Versione release
gcc programma.c -o programma_release
GCC permette di inserire codice assembly direttamente nel C.
#include <stdio.h>
int main() {
int input = 5;
int output;
// Inline assembly AT&T syntax (Linux)
asm("movl %1, %%eax\n\t"
"addl $10, %%eax\n\t"
"movl %%eax, %0"
: "=r" (output)
: "r" (input)
: "%eax");
printf("Input: %d, Output: %d\n", input, output);
return 0;
}
int main() {
int a = 10, b = 20, sum;
asm volatile(
".intel_syntax noprefix\n\t"
"mov eax, %1\n\t"
"add eax, %2\n\t"
"mov %0, eax\n\t"
".att_syntax prefix"
: "=r" (sum)
: "r" (a), "r" (b)
: "eax"
);
printf("Somma: %d\n", sum);
return 0;
}
GCC supporta plugin per estenderne le funzionalità.
// myplugin.c
#include "gcc-plugin.h"
#include "plugin-version.h"
int plugin_is_GPL_compatible;
int plugin_init(struct plugin_name_args *plugin_info,
struct plugin_gcc_version *version) {
printf("Plugin caricato!\n");
return 0;
}
Compilazione del plugin:
gcc -I$(gcc -print-file-name=plugin)/include \
-fPIC -shared myplugin.c -o myplugin.so
Uso del plugin:
gcc -fplugin=./myplugin.so programma.c -o programma
Misurare quale codice viene eseguito durante i test.
# Compilare con coverage
gcc -fprofile-arcs -ftest-coverage programma.c -o programma
# Eseguire il programma
./programma
# Generare report (lcov)
gcov programma.c
# Visualizzare
cat programma.c.gcov
Output mostra numero di esecuzioni per ogni linea.
Per report HTML:
# Installare lcov
sudo apt install lcov
# Generare dati
lcov --capture --directory . --output-file coverage.info
# Generare HTML
genhtml coverage.info --output-directory coverage_html
# Aprire nel browser
firefox coverage_html/index.html
// scientific_calc.c
#include <stdio.h>
#include <math.h>
#define PI 3.14159265358979323846
int main() {
double angle = 45.0;
double radians = angle * PI / 180.0;
printf("Angolo: %.2f gradi\n", angle);
printf("Seno: %.4f\n", sin(radians));
printf("Coseno: %.4f\n", cos(radians));
printf("Tangente: %.4f\n", tan(radians));
double x = 2.0;
printf("\nEsponenziale e^%.1f = %.4f\n", x, exp(x));
printf("Logaritmo naturale ln(%.1f) = %.4f\n", x, log(x));
printf("Radice quadrata sqrt(%.1f) = %.4f\n", x, sqrt(x));
return 0;
}
Compilazione:
gcc scientific_calc.c -o scientific_calc -lm -Wall -O2
./scientific_calc
// parallel_sum.c
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#define NUM_THREADS 4
#define ARRAY_SIZE 1000000
typedef struct {
int *array;
int start;
int end;
long long partial_sum;
} ThreadData;
void* compute_partial_sum(void* arg) {
ThreadData* data = (ThreadData*)arg;
data->partial_sum = 0;
for (int i = data->start; i < data->end; i++) {
data->partial_sum += data->array[i];
}
return NULL;
}
int main() {
int *array = malloc(ARRAY_SIZE * sizeof(int));
pthread_t threads[NUM_THREADS];
ThreadData thread_data[NUM_THREADS];
// Inizializzare array
for (int i = 0; i < ARRAY_SIZE; i++) {
array[i] = i + 1;
}
// Dividere lavoro tra thread
int chunk_size = ARRAY_SIZE / NUM_THREADS;
for (int i = 0; i < NUM_THREADS; i++) {
thread_data[i].array = array;
thread_data[i].start = i * chunk_size;
thread_data[i].end = (i == NUM_THREADS - 1) ?
ARRAY_SIZE : (i + 1) * chunk_size;
pthread_create(&threads[i], NULL,
compute_partial_sum, &thread_data[i]);
}
// Attendere completamento
long long total_sum = 0;
for (int i = 0; i < NUM_THREADS; i++) {
pthread_join(threads[i], NULL);
total_sum += thread_data[i].partial_sum;
}
printf("Somma totale: %lld\n", total_sum);
printf("Formula: %lld\n", (long long)ARRAY_SIZE * (ARRAY_SIZE + 1) / 2);
free(array);
return 0;
}
Compilazione:
gcc parallel_sum.c -o parallel_sum -lpthread -Wall -O2
./parallel_sum
// file_manager.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define BUFFER_SIZE 1024
int copy_file(const char* source, const char* dest) {
FILE *src = fopen(source, "rb");
if (src == NULL) {
fprintf(stderr, "Errore apertura %s: %s\n",
source, strerror(errno));
return -1;
}
FILE *dst = fopen(dest, "wb");
if (dst == NULL) {
fprintf(stderr, "Errore apertura %s: %s\n",
dest, strerror(errno));
fclose(src);
return -1;
}
char buffer[BUFFER_SIZE];
size_t bytes;
while ((bytes = fread(buffer, 1, BUFFER_SIZE, src)) > 0) {
if (fwrite(buffer, 1, bytes, dst) != bytes) {
fprintf(stderr, "Errore scrittura: %s\n", strerror(errno));
fclose(src);
fclose(dst);
return -1;
}
}
if (ferror(src)) {
fprintf(stderr, "Errore lettura: %s\n", strerror(errno));
fclose(src);
fclose(dst);
return -1;
}
fclose(src);
fclose(dst);
return 0;
}
int main(int argc, char *argv[]) {
if (argc != 3) {
fprintf(stderr, "Uso: %s <sorgente> <destinazione>\n", argv[0]);
return EXIT_FAILURE;
}
printf("Copiando %s -> %s...\n", argv[1], argv[2]);
if (copy_file(argv[1], argv[2]) == 0) {
printf("Copia completata con successo!\n");
return EXIT_SUCCESS;
} else {
fprintf(stderr, "Copia fallita!\n");
return EXIT_FAILURE;
}
}
Compilazione:
gcc file_manager.c -o file_manager -Wall -Wextra -O2
# Test
echo "Test content" > test.txt
./file_manager test.txt test_copy.txt
cat test_copy.txt
// cmdline_tool.c
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <string.h>
void print_usage(const char* program_name) {
printf("Uso: %s [opzioni]\n", program_name);
printf("Opzioni:\n");
printf(" -h, --help Mostra questo help\n");
printf(" -v, --verbose Modalità verbose\n");
printf(" -o, --output FILE File di output\n");
printf(" -n, --number NUM Numero di iterazioni\n");
}
int main(int argc, char *argv[]) {
int verbose = 0;
char *output_file = NULL;
int iterations = 10;
static struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"verbose", no_argument, 0, 'v'},
{"output", required_argument, 0, 'o'},
{"number", required_argument, 0, 'n'},
{0, 0, 0, 0}
};
int opt;
while ((opt = getopt_long(argc, argv, "hvo:n:",
long_options, NULL)) != -1) {
switch (opt) {
case 'h':
print_usage(argv[0]);
return EXIT_SUCCESS;
case 'v':
verbose = 1;
break;
case 'o':
output_file = optarg;
break;
case 'n':
iterations = atoi(optarg);
break;
default:
print_usage(argv[0]);
return EXIT_FAILURE;
}
}
if (verbose) {
printf("Modalità verbose attiva\n");
printf("Iterazioni: %d\n", iterations);
if (output_file)
printf("Output file: %s\n", output_file);
}
// Logica principale del programma
for (int i = 0; i < iterations; i++) {
if (verbose)
printf("Iterazione %d/%d\n", i + 1, iterations);
}
printf("Completato!\n");
return EXIT_SUCCESS;
}
Compilazione e uso:
gcc cmdline_tool.c -o cmdline_tool -Wall
# Test varie opzioni
./cmdline_tool --help
./cmdline_tool -v -n 5
./cmdline_tool --verbose --output result.txt --number 3
Problema: Funzione dichiarata ma non implementata o libreria non linkata.
Soluzione:
# Se manca implementazione, aggiungere il file .c
gcc main.c funzione.c -o programma
# Se manca libreria (es. math)
gcc programma.c -o programma -lm
# Se libreria in directory custom
gcc programma.c -L/path/to/lib -lmylib -o programma
Problema: Libreria non trovata.
Soluzione:
# Installare la libreria di sviluppo
sudo apt install lib<nome>-dev
# O specificare percorso
gcc programma.c -L/usr/local/lib -lmylib -o programma
# Verificare se libreria esiste
ldconfig -p | grep <library>
Problema: File header non trovato.
Soluzione:
# Specificare directory include
gcc -I/path/to/headers programma.c -o programma
# Per progetti complessi
gcc -I./include -I/usr/local/include programma.c -o programma
Debug:
# Compilare con debug symbols
gcc -g programma.c -o programma
# Usare GDB
gdb ./programma
(gdb) run
(gdb) backtrace
(gdb) quit
# Oppure usare AddressSanitizer
gcc -fsanitize=address -g programma.c -o programma
./programma
Debug:
# Usare Valgrind
sudo apt install valgrind
valgrind --leak-check=full ./programma
# O usare LeakSanitizer
gcc -fsanitize=leak -g programma.c -o programma
./programma
Debug:
# UndefinedBehaviorSanitizer
gcc -fsanitize=undefined -g programma.c -o programma
./programma
Problema: Libreria condivisa non trovata a runtime.
Soluzione:
# Metodo 1: Aggiungere a LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/path/to/lib:$LD_LIBRARY_PATH
# Metodo 2: Configurare ldconfig
sudo echo "/path/to/lib" > /etc/ld.so.conf.d/mylib.conf
sudo ldconfig
# Metodo 3: Usare rpath
gcc programma.c -L. -lmylib -Wl,-rpath,'$ORIGIN/lib' -o programma
Problema: Funzione usata senza prototipo.
Soluzione:
// Aggiungere #include appropriato
#include <string.h> // Per strlen, strcpy, etc.
#include <stdlib.h> // Per malloc, free, etc.
#include <math.h> // Per sqrt, sin, cos, etc.
Problema: Confronto tra signed e unsigned.
Soluzione:
// Cast esplicito
size_t size = strlen(str);
for (int i = 0; i < (int)size; i++) { ... }
// O usare tipo corretto
for (size_t i = 0; i < size; i++) { ... }
Debug:
# Compilare con profiling
gcc -pg -O2 programma.c -o programma
# Eseguire
./programma
# Analizzare con gprof
gprof programma gmon.out > analysis.txt
less analysis.txt
Soluzione:
# Strippare simboli di debug
strip programma
# O compilare senza debug
gcc -Os programma.c -o programma
# Link Time Optimization
gcc -flto -Os programma.c -o programma
Questa guida ha coperto GCC dalle basi all’utilizzo avanzato:
GCC è uno strumento estremamente potente e flessibile. La padronanza delle sue opzioni e caratteristiche permette di:
# Compilazione base
gcc programma.c -o programma
# Con warning e ottimizzazione
gcc -Wall -Wextra -O2 programma.c -o programma
# Debug completo
gcc -g -Wall -Wextra -fsanitize=address,undefined programma.c -o programma
# Release ottimizzata
gcc -O3 -DNDEBUG -Wall -Wextra programma.c -o programma
# Libreria condivisa
gcc -fPIC -shared mylib.c -o libmylib.so
# Multi-file
gcc -I./include src/*.c -o programma -lm
# Cross-compilation
arm-linux-gnueabihf-gcc programma.c -o programma_arm
Buon coding!